<template>
  <view
    :id="id"
    class="tn-sticky"
    :style="stickyStyle"
  >
    <view class="tn-sticky__content" :style="contentStyle">
      <slot></slot>
    </view>
  </view>
</template>

<script lang="ts" setup>
import { stickyProps, stickyEmits } from '../../../types/tn-sticky'
import { computed, watch, getCurrentInstance, ComponentInternalInstance, nextTick, CSSProperties, onMounted, ref } from 'vue'
import { handleValueWithUnit, uuid, getRectInfo } from '../../../utils'
import useObserver from '../lib/useObserver'
import useSupportSticky from '../lib/useSupportSticky'

const props = defineProps(stickyProps)
const emits = defineEmits(stickyEmits)

// sticky吸顶样式
const stickyStyle = computed<CSSProperties>(() => {
  let style: CSSProperties = {}

  if (props?.zIndex) {
    style.zIndex = Number(props.zIndex)
  }

  if (props.enabled) {
    if (cssSticky.value) {
      style.position = 'sticky'
      style.top = stickTop.value + 'px'
    } else {
      style.height = fixedStatus.value ? handleValueWithUnit(contentHeight.value, 'px') : 'auto'
    }
  } else {
    // 不需要吸顶的情况下设置默认position
    // #ifdef APP-NVUE
    style.position = 'relative'
    // #endif
    // #ifndef APP-NVUE
    style.position = 'static'
    // #endif
  }

  return style
})
// stickyContent样式
const contentStyle = computed<CSSProperties>(() => {
  let style: CSSProperties = {}
  if (!cssSticky.value) {
    style.position = fixedStatus.value ? 'fixed' : 'static'
    style.top = stickTop.value + 'px'
    style.left = contentLeft.value + 'px'
    style.width = handleValueWithUnit(contentWidth.value, 'px')
  }
  if (stickTop.value > 0) {
    style.top = stickTop.value + 'px'
  }

  return style
})

// 吸顶高度
const stickTop = computed<number>(() => {
  let top: number = 0
  if (props?.top !== 'undefined') {
    top += Number(props.top)
  }
  if (props?.customNavbarHeight !== 'undefined') {
    top += Number(props.customNavbarHeight)
  }
  return top
})


const instance = getCurrentInstance() as ComponentInternalInstance
// 组件唯一标识
const id = uuid()

// 监听组件布局状态
const { connectObserver, disconnectObserver } = useObserver(instance)
// 判断是否支持CSS Sticky
const { checkCSSStickySupport } = useSupportSticky(instance)
let cssSticky = ref<boolean>(false)

// 初始化组件
const initSticky = async () => {

  // 获取使用模式
  cssSticky.value = await checkCSSStickySupport(`#${id}`)

  // 如果不支持css sticky则使用js模式
  if(!cssSticky.value) {
    props?.enabled && initObserver()
  }
}

// 初始化节点布局状态
let initCount = 0
// 内容区域的节点信息，防止在js模式下设置为fixed后元素塌陷无法再进行交互判断
let contentWidth = ref<number | string>('auto')
let contentHeight = ref<number | string>('auto')
let contentLeft = ref<number>(0)
const initObserver = () => {
  disconnectObserver()
  getRectInfo(`#${id}`, instance).then((res: UniApp.NodeInfo | null) => {
    if (!res) {
      if (initCount > 10) {
        console.error('[tn-sticky]初始化组件失败, 找不到组件');
        return
      }
      setTimeout(() => {
        initSticky()
        initCount++
      }, 50)
      return
    }

    initCount = 0
    contentWidth.value = res.width || 0
    contentHeight.value = res.height || 0
    contentLeft.value = res.left || 0
    nextTick(() => {
      connectObserver(`#${id}`, -stickTop.value, (observerRes: UniApp.ObserveResult) => {
        if (!props.enabled) return
        setFixed(observerRes.boundingClientRect.top)
      })
    })
  })
}

// 监听吸顶高度
watch(() => [props.top, props.customNavbarHeight], () => {
  // 重新初始化监听器
  initSticky()
})

// 监听是否吸顶
watch(() => props.enabled, (val: boolean) => {
  if (!val) {
    fixedStatus.value = false,
    disconnectObserver()
  } else {
    initSticky()
  }
})

// 固定状态
let fixedStatus = ref<boolean>(false)
// 设置是否固定
const setFixed = (top: number) => {
  const status = top <= stickTop.value
  if (status) {
    emits('fixed', props.index)
  } else if (fixedStatus) {
    emits('unfixed', props.index)
  }

  fixedStatus.value = status
}

onMounted(() => {
  nextTick(() => {
    setTimeout(() => {
      initSticky()
    }, 50);
  })
})

</script>

<script lang="ts">
export default {
  options: {
    // 在微信小程序中将组件节点渲染为虚拟节点，更加接近Vue组件的表现(不会出现shadow节点下再去创建元素)
    virtualHost: true
  }
}
</script>

<style lang="scss" scoped>

.tn-sticky {
  /* #ifdef APP-VUE || MP-WEIXIN */
  position: sticky;
  /* #endif */
}
</style>
